package com.nio;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;

/**
 * 传统的数据传输是通过DMA 去连接(内存和IO接口进行处理的)  ==> DMA总线
 *
 * 6.通道(channel)
 *  完全独立的处理器, 专门处理IO操作, 替代CPU的处理
 *
 *  (1).作用: 用于源节点和目标节点的连接, 在 Java NIO 中负责缓冲区的数据传输. 本身不存储数据
 *
 *  (2).通道的主要实现类
 *  java.nio.channels.Channel 接口
 *      |--FileChannel
 *      |--SocketChannel
 *      |--ServerSocketChannel
 *      |--DatagramChannel
 *  (3).获取通道
 *    1. java提供了getChannel()方法 来获取通道的类
 *
 *     本地IO:
 *      FileInputStream/FileOutputStream
 *      RandomAccessFile
 *
 *     网络IO:
 *      Socket
 *      ServerSocket
 *      DatagramSocket
 *
 *   2.java1.7 中提供了NIO.2, 针对各个通道提供了静态方法open();
 *
 *   3.java1.7 中NIO.2 中的 Files工具内中提供了newByteChannel();
 *
 * (4)通道之间的数据传输
 * transferTo()
 * transferFrom()
 *
 * (5)分散(Scatter) 和 聚集(Gather)
 *
 *   分散读取:  从Channel(通道) 中读取的数据 "分散" 到 多个 Buffer(缓冲) 中
 *
 *   聚集写入:  将多个 Buffer(缓冲) 的 数据 "聚集" 到 Channel(通道)中
 *
 * (6) 字符集: Charset
 *
 * 编码: 字符串  --> 字节数组
 * 解码: 字节数组  --> 字符串
 *
 * @author Administrator
 */
public class TestChannel {


    @Test
    public void testChannels() throws CharacterCodingException {

        Charset cs1 = Charset.forName("GBK");

        //获取编码器
        CharsetEncoder ce = cs1.newEncoder();

        //获取解码器
        CharsetDecoder cd = cs1.newDecoder();

        //创建缓冲区
        CharBuffer cbuf = CharBuffer.allocate(1024);
        cbuf.put("我就是相关的缓存");
        cbuf.flip();


        //编码
        ByteBuffer bBuf = ce.encode(cbuf);

        for(byte bs : bBuf.array()){
            //编码的结果
            System.out.println(bs);
        }

        //解码
        CharBuffer charBuffer =  cd.decode(bBuf);
        //bBuf.flip();
        System.out.println(charBuffer.toString());

        System.out.println("=================");


        Charset charset = Charset.forName("UTF-8");
        //CharsetDecoder cd2 =  charset.newDecoder();
        bBuf.flip();
        CharBuffer charBuffer1 = charset.decode(bBuf);
        System.out.println(charBuffer1.toString());
    }

    /**
     * 字符集
     */
    @Test
    public void testChannel5(){

        //查看所有的字符集
        Map<String, Charset> charsetMap = Charset.availableCharsets();

        Set<Map.Entry<String, Charset>> set = charsetMap.entrySet();

        for(Map.Entry<String, Charset> charset : set){
            System.out.println(charset.getKey() + "=====" + charset.getValue());
        }


    }

    /**
     * 分散和聚集
     */
    @Test
    public void testChannel4() throws IOException {

        RandomAccessFile randomAccessFile  = new RandomAccessFile("e:/1.txt", "rw");

        //获取通道
        FileChannel channel = randomAccessFile.getChannel();

        //创建多个缓冲区
        ByteBuffer b1 = ByteBuffer.allocate(100);
        ByteBuffer b2 = ByteBuffer.allocate(1024);

        ByteBuffer[] bbs = {b1,b2};
        //分散读取
        channel.read(bbs);

        //读取转换
        for(ByteBuffer byteBuffer : bbs){
            byteBuffer.flip();
        }
        System.out.println(new String(bbs[0].array(), 0 , bbs[0].limit()));
        System.out.println("=======================================");
        System.out.println(new String(bbs[1].array(), 0, bbs[1].limit()));

        System.out.println(bbs[1].capacity());


        //聚集写入
        RandomAccessFile writeFile  = new RandomAccessFile("e:/2.txt", "rw");

        //获取通道
        FileChannel channel2 = writeFile.getChannel();

        //聚集写入
        channel2.write(bbs);

    }


    /**
     * 利用通道实现文件的复制 (直接缓冲区) ,直接操作通道
     * @throws IOException
     */
    @Test
    public void testChannel3() throws IOException {

        long start = System.currentTimeMillis();
        FileChannel inChannel = FileChannel.open(Paths.get("e:/big.rpm"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("e:/big3.rpm"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

        //文件的传输
        //inChannel.transferTo(0,inChannel.size(),outChannel);
        outChannel.transferFrom(inChannel, 0, inChannel.size());

        long end = System.currentTimeMillis();

        System.out.println("直接缓冲区消耗时间为:" + (end - start));
        inChannel.close();
        outChannel.close();
    }

    /**
     * 经过通道 直接缓冲区复制文件 (物理内存映射文件)
     */
    @Test
    public void testChannel2() throws IOException {


        long start = System.currentTimeMillis();

        FileChannel inChannel = FileChannel.open(Paths.get("e:/big.rpm"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("e:/big1.rpm"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        //创建映射文件, 操作直接缓冲区
        MappedByteBuffer inMapBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0 , inChannel.size());
        MappedByteBuffer outMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        byte[] dst = new byte[inMapBuffer.limit()];
        inMapBuffer.get(dst);
        outMapBuffer.put(dst);

        long end = System.currentTimeMillis();

        System.out.println("操作直接缓冲区的时间为" + (end - start));



        inChannel.close();
        outChannel.close();


    }

    /**
     * 采用通道进行复制文件, (非直接缓冲区)的方式
     */
    @Test
    public void testChannel1(){
        long start = System.currentTimeMillis();
        //创建文件流
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;

        //创建通道
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inputStream = new FileInputStream("e:/big.rpm");
            outputStream = new FileOutputStream("e:/big1.rpm");

            inChannel = inputStream.getChannel();
            outChannel = outputStream.getChannel();

            //创建 缓存区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            //缓存区的数据交换读与写
            while(inChannel.read(byteBuffer) != -1){
                byteBuffer.flip();
                outChannel.write(byteBuffer);
                byteBuffer.clear();
            }
            long end = System.currentTimeMillis();
            System.out.println("消耗时间为:" + (end - start));
        }catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(inChannel != null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outChannel != null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
